package main

import (
	"HandWritingJVM/instructions"
	"HandWritingJVM/instructions/base"
	"HandWritingJVM/rtda"
	"HandWritingJVM/rtda/heap"
	"fmt"
)

func interpret(method *heap.Method, logInst bool) {
	thread := rtda.NewThread()
	frame := thread.NewFrame(method)
	thread.PushFrame(frame)
	defer catchErr(thread)
	loop(thread, logInst)
}

func loop(thread *rtda.Thread, logInst bool) {
	reader := base.BytecodeReader{}
	for true {
		frame := thread.CurrentFrame()
		pc := frame.NextPC()
		thread.SetPC(pc)
		reader.Reset(frame.Method().Code(), pc)
		opcode := reader.ReadUint8()
		inst := instructions.NewInstruction(opcode)
		inst.FetchOperands(&reader)
		frame.SetNextPC(reader.PC())
		if logInst {
			logInstruction(opcode,frame, inst)
		}
		//fmt.Printf("%d %x\n",pc,opcode)
		inst.Execute(frame)
		if thread.IsStackEmpty() {
			break
		}
	}
}

func logInstruction(opcode uint8 ,frame *rtda.Frame, inst base.Instruction) {
	method := frame.Method()
	className := method.Class().Name()
	methodName := method.Name()
	pc := frame.Thread().PC()
	fmt.Printf("#%2d opcode: %2x %T %v %v.%v() \n",pc,opcode, inst, inst, className, methodName)
}

func catchErr(thread *rtda.Thread) {
	if r := recover(); r != nil {
		logFrames(thread)
		panic(r)
	}
}

func logFrames(thread *rtda.Thread) {
	for !thread.IsStackEmpty() {
		frame := thread.PopFrame()
		method := frame.Method()
		fmt.Printf(">> pc:%4d %v.%v%v %v\n", frame.NextPC(), method.Class().Name(), method.Name(), method.Descriptor(),frame.LocalVars())
	}
}
